<!DOCTYPE html>
<head>
    <meta charset="utf-8">

    <script src="/vendor/d3/d3-3.3.4.js"></script>
    <script src="/vendor/dagre-d3/dagre-d3.min.js"></script>
    <script src="/vendor/d3-tip/d3.tip.v0.6.3.js"></script>

    <link href="/vendor/bootstrap/css/bootstrap.css" rel="stylesheet" type="text/css">

    <style>
        svg {
            overflow: hidden;
        }

        g.state-RUNNABLE > rect {
            fill: #00c100;
        }

        g.state-TIMED_WAITING > rect {
            fill: #ecec00;
        }

        g.state-WAITING > rect {
            fill: #ecec00;
        }

        g.state-BLOCKED > rect {
            fill: rgb(255, 0, 11);
        }

        .node rect {
            stroke: #333;
            stroke-width: 1px;
            fill: #fff;
        }

        .edgeLabel rect {
            fill: #fff;
        }

        .edgePath {
            stroke: #333;
            stroke-width: 1.5px;
            fill: none;
        }

        .d3-tip {
            /*line-height: 1;*/
            /*font-weight: bold;*/
            padding: 12px;
            background: rgb(92, 92, 92);
            color: #fff;
            border-radius: 2px;
        }

        /* Creates a small triangle extender for the tooltip */
        .d3-tip:after {
            box-sizing: border-box;
            display: inline;
            font-size: 10px;
            width: 100%;
            line-height: 1;
            color: rgba(0, 0, 0, 0.8);
            content: "\25BC";
            position: absolute;
            text-align: center;
        }

        /* Style northward tooltips differently */
        .d3-tip.n:after {
            margin: -1px 0 0 0;
            top: 100%;
            left: 0;
        }
    </style>
</head>

<body>

<div class="container">
    <svg width="800" height="600" id="graph">
        <g transform="translate(20, 20)"/>
    </svg>
</div>
</body>

<script>

var graph;

var tip = d3.tip()
        .attr('class', 'd3-tip')
        .offset([-10, 0])
        .html(function (u) {
            var text = graph.node(u)
                    .stacktrace
                    .slice(0, 20)
                    .map(function (e) {
                        var line = e.line < 1 ? "&lt;native&gt;" : e.line;
                        return e.className + "." + e.method + " (" + e.file + ":" + line + ")";
                    })
                    .join("<br>");
            if (graph.node(u).stacktrace.length > 20) {
                text += "<br>...";
            }
            return text;
        });


d3.select("#graph").call(tip);

function redraw() {

    d3.json('/v1/thread', function (error, json) {
        if (error) {
            setTimeout(redraw, 1000);
            return;
        }

        var threads = json;

        graph = new dagreD3.Digraph();

        threads.forEach(function (thread) {
            graph.addNode(thread.id, {
                label: thread.name,
                nodeclass: "state-" + thread.state,
                stacktrace: thread.stackTrace
            });
        });

        threads.forEach(function (thread) {
            if (thread.lockOwnerId) {
                graph.addEdge(null, thread.id, thread.lockOwnerId);
            }
        });


        var renderer = new dagreD3.Renderer()
                .transition(function (selection) {
                    return selection.transition().duration(500);
                });


        var originalDrawNodes = renderer.drawNodes();
        renderer.drawNodes(function (graph, root) {
            var svgNodes = originalDrawNodes(graph, root);

            svgNodes.each(function (u) {
                var node = this;

                // remove old classes
                ["RUNNABLE", "TIMED_WAITING", "WAITING", "BLOCKED"].forEach(function (state) {
                    d3.select(node).classed("state-" + state, false)
                });

                d3.select(this).classed(graph.node(u).nodeclass, true);

                d3.select(this)
                        .on('mouseover', tip.show)
                        .on('mouseout', tip.hide);

            });

            return svgNodes;
        });


        var layout = renderer
                .layout(dagreD3.layout()
                        .nodeSep(10)
                        .rankDir("LR"))
                .run(graph, d3.select("#graph g"));


        d3.select("#graph")
                .attr("width", layout.graph().width + 40)
                .attr("height", layout.graph().height + 40);


        setTimeout(redraw, 1000);
    });
}

redraw();

</script>
